Projet IA : Tremblements de terre 2023 mondiaux¶



Cours: Projet Data Science & Intelligence Artificielle



Professeur: Nancy CHENDEB



Date de rendu: 28 Janvier 2024



Trinôme: Nicolas RENOIR (CCC) 33% - Killian FOURNIER (CCC) 33% - Lucas LOPES (CCC) 33%



Sommaire:

1- Nettoyage et prétraitement des données

2- Analyse exploratoire des données (EDA)

3- Visualisation

4- Sélection de caractéristiques cibles

5- Sélection du modèle

6- Entraînement du modèle

7- Evaluation du modèle

8- Optimisation, maintenance et mise à jour





Chargement des données¶

In [1]:
import pandas as pd
import numpy as np

df = pd.read_csv('earthquakes_2023_global_bruite.csv', delimiter = ';')

df
Out[1]:
time latitude longitude depth mag magType nst gap dmin rms ... updated place type horizontalError depthError magError magNst status locationSource magSource
0 2023-01-01T00:49:25.294Z 52.0999 178.5218 82.770 3.10 ml 14.0 139.0 0.8700 0.18 ... 2023-03-11T22:51:52.040Z Rat Islands, Aleutian Islands, Alaska earthquake 8.46 21.213 0.097 14.0 reviewed us us
1 2023-01-01T01:41:43.755Z 7.1397 126.7380 79.194 4.50 mb 32.0 104.0 1.1520 0.47 ... 2023-03-11T22:51:45.040Z 23 km ESE of Manay, Philippines earthquake 5.51 7.445 0.083 43.0 reviewed us us
2 2023-01-01T03:29:31.070Z 19.1631 -66.5251 24.000 3.93 md 23.0 246.0 0.8479 0.22 ... 2023-03-11T22:51:29.040Z Puerto Rico region earthquake 0.91 15.950 0.090 16.0 reviewed pr pr
3 2023-01-01T04:09:32.814Z -4.7803 NaN 63.787 4.30 mb 17.0 187.0 0.4570 0.51 ... 2023-03-11T22:51:45.040Z 99 km SSW of Pagar Alam, Indonesia earthquake 10.25 6.579 0.238 5.0 reviewed us us
4 2023-01-01T04:29:13.793Z 53.3965 -166.9417 10.000 3.00 ml 19.0 190.0 0.4000 0.31 ... 2023-03-11T22:51:38.040Z 59 km SSW of Unalaska, Alaska earthquake 1.41 1.999 0.085 18.0 reviewed us us
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
26637 2023-12-29T03:37:19.334Z -6.9527 154.9829 10.000 5.20 mb 72.0 60.0 3.9240 0.93 ... 2023-12-29T04:05:57.040Z 89 km SW of Panguna, Papua New Guinea earthquake 10.07 1.765 0.048 141.0 reviewed us us
26638 2023-12-29T04:38:54.109Z 32.3262 141.7386 10.000 5.10 mb 74.0 121.0 1.8030 0.70 ... 2023-12-29T10:59:44.533Z Izu Islands, Japan region earthquake 9.17 1.870 0.042 187.0 reviewed us us
26639 2023-12-29T08:42:05.747Z -7.2411 68.0663 10.000 5.10 mb 60.0 54.0 12.7760 0.57 ... 2023-12-29T08:57:05.040Z Chagos Archipelago region earthquake 8.02 1.792 0.090 40.0 reviewed us us
26640 2023-12-29T11:02:48.679Z -19.1602 169.0428 153.264 4.70 mb 40.0 61.0 3.7460 0.82 ... 2023-12-29T11:22:46.040Z 49 km NNW of Isangel, Vanuatu earthquake 8.52 7.433 0.081 46.0 reviewed us us
26641 2023-12-29T16:31:16.656Z 25.1050 96.5309 10.000 5.00 mb 53.0 64.0 4.1560 0.78 ... 2023-12-29T16:45:27.040Z 92 km WSW of Myitkyina, Myanmar earthquake 7.85 1.876 0.071 64.0 reviewed us us

26642 rows × 22 columns

Nous avons remarqué que le dataset ne se chargeait pas correctement lorsque nous mettions une , en délimiteur (certaines valeurs se retoruvaient dans une seule colonne). En explorant, nous avons remarqué que certaines lignes avaient des "dans les colonnes ce qui nous empêchait de lire les données correctement. Pour régler ça, nous avons ouvert le dataset sur Excel pour le transformer avec , comme délimiteur. Puis nous l'avons de nouveau ouvert avec Python en utilisant ; comme délimiteur.

1 - Nettoyage et prétraitement des données¶

Le nettoyage de données est une étape importante lorsque l'on souhaite analyser des données. Il sert à préparer et à traiter les données brutes pour les rendre utilisables et représentatives (pour les modéliser, les comparer...).

Afin de garantir un bon traitement et une bonne analyse des données, il faut nettoyer notre dataset. Après avoir visualisé ce dernier, nous avons observé la présence de lignes identiques. Pour garantir la fiabilité de nos analyses, nous avons supprimé ces lignes pour ne pas fausser les résultats.

In [2]:
df = df.drop_duplicates()
df
Out[2]:
time latitude longitude depth mag magType nst gap dmin rms ... updated place type horizontalError depthError magError magNst status locationSource magSource
0 2023-01-01T00:49:25.294Z 52.0999 178.5218 82.770 3.10 ml 14.0 139.0 0.8700 0.18 ... 2023-03-11T22:51:52.040Z Rat Islands, Aleutian Islands, Alaska earthquake 8.46 21.213 0.097 14.0 reviewed us us
1 2023-01-01T01:41:43.755Z 7.1397 126.7380 79.194 4.50 mb 32.0 104.0 1.1520 0.47 ... 2023-03-11T22:51:45.040Z 23 km ESE of Manay, Philippines earthquake 5.51 7.445 0.083 43.0 reviewed us us
2 2023-01-01T03:29:31.070Z 19.1631 -66.5251 24.000 3.93 md 23.0 246.0 0.8479 0.22 ... 2023-03-11T22:51:29.040Z Puerto Rico region earthquake 0.91 15.950 0.090 16.0 reviewed pr pr
3 2023-01-01T04:09:32.814Z -4.7803 NaN 63.787 4.30 mb 17.0 187.0 0.4570 0.51 ... 2023-03-11T22:51:45.040Z 99 km SSW of Pagar Alam, Indonesia earthquake 10.25 6.579 0.238 5.0 reviewed us us
4 2023-01-01T04:29:13.793Z 53.3965 -166.9417 10.000 3.00 ml 19.0 190.0 0.4000 0.31 ... 2023-03-11T22:51:38.040Z 59 km SSW of Unalaska, Alaska earthquake 1.41 1.999 0.085 18.0 reviewed us us
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
26637 2023-12-29T03:37:19.334Z -6.9527 154.9829 10.000 5.20 mb 72.0 60.0 3.9240 0.93 ... 2023-12-29T04:05:57.040Z 89 km SW of Panguna, Papua New Guinea earthquake 10.07 1.765 0.048 141.0 reviewed us us
26638 2023-12-29T04:38:54.109Z 32.3262 141.7386 10.000 5.10 mb 74.0 121.0 1.8030 0.70 ... 2023-12-29T10:59:44.533Z Izu Islands, Japan region earthquake 9.17 1.870 0.042 187.0 reviewed us us
26639 2023-12-29T08:42:05.747Z -7.2411 68.0663 10.000 5.10 mb 60.0 54.0 12.7760 0.57 ... 2023-12-29T08:57:05.040Z Chagos Archipelago region earthquake 8.02 1.792 0.090 40.0 reviewed us us
26640 2023-12-29T11:02:48.679Z -19.1602 169.0428 153.264 4.70 mb 40.0 61.0 3.7460 0.82 ... 2023-12-29T11:22:46.040Z 49 km NNW of Isangel, Vanuatu earthquake 8.52 7.433 0.081 46.0 reviewed us us
26641 2023-12-29T16:31:16.656Z 25.1050 96.5309 10.000 5.00 mb 53.0 64.0 4.1560 0.78 ... 2023-12-29T16:45:27.040Z 92 km WSW of Myitkyina, Myanmar earthquake 7.85 1.876 0.071 64.0 reviewed us us

24682 rows × 22 columns

On remarque que certaines colonnes sont constituées pour certaines de NaN on décide donc de les supprimer. On effectue cela car elles ne nous sont pas utiles dans notre analyse (Ex : Si nous n'avons pas toutes les coordonnées géographiques nous ne pouvons correctement analyser) . Pour cela on execute le code suivant :

In [3]:
df = df.dropna()
df
Out[3]:
time latitude longitude depth mag magType nst gap dmin rms ... updated place type horizontalError depthError magError magNst status locationSource magSource
0 2023-01-01T00:49:25.294Z 52.0999 178.5218 82.770 3.10 ml 14.0 139.0 0.8700 0.18 ... 2023-03-11T22:51:52.040Z Rat Islands, Aleutian Islands, Alaska earthquake 8.46 21.213 0.097 14.0 reviewed us us
1 2023-01-01T01:41:43.755Z 7.1397 126.7380 79.194 4.50 mb 32.0 104.0 1.1520 0.47 ... 2023-03-11T22:51:45.040Z 23 km ESE of Manay, Philippines earthquake 5.51 7.445 0.083 43.0 reviewed us us
2 2023-01-01T03:29:31.070Z 19.1631 -66.5251 24.000 3.93 md 23.0 246.0 0.8479 0.22 ... 2023-03-11T22:51:29.040Z Puerto Rico region earthquake 0.91 15.950 0.090 16.0 reviewed pr pr
4 2023-01-01T04:29:13.793Z 53.3965 -166.9417 10.000 3.00 ml 19.0 190.0 0.4000 0.31 ... 2023-03-11T22:51:38.040Z 59 km SSW of Unalaska, Alaska earthquake 1.41 1.999 0.085 18.0 reviewed us us
5 2023-01-01T04:50:17.639Z 19.2811 -155.4282 37.751 2.80 ml 19.0 127.0 0.0660 0.18 ... 2023-03-11T22:51:29.040Z 10 km NNE of P?hala, Hawaii earthquake 2.77 5.266 0.060 36.0 reviewed us us
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
26637 2023-12-29T03:37:19.334Z -6.9527 154.9829 10.000 5.20 mb 72.0 60.0 3.9240 0.93 ... 2023-12-29T04:05:57.040Z 89 km SW of Panguna, Papua New Guinea earthquake 10.07 1.765 0.048 141.0 reviewed us us
26638 2023-12-29T04:38:54.109Z 32.3262 141.7386 10.000 5.10 mb 74.0 121.0 1.8030 0.70 ... 2023-12-29T10:59:44.533Z Izu Islands, Japan region earthquake 9.17 1.870 0.042 187.0 reviewed us us
26639 2023-12-29T08:42:05.747Z -7.2411 68.0663 10.000 5.10 mb 60.0 54.0 12.7760 0.57 ... 2023-12-29T08:57:05.040Z Chagos Archipelago region earthquake 8.02 1.792 0.090 40.0 reviewed us us
26640 2023-12-29T11:02:48.679Z -19.1602 169.0428 153.264 4.70 mb 40.0 61.0 3.7460 0.82 ... 2023-12-29T11:22:46.040Z 49 km NNW of Isangel, Vanuatu earthquake 8.52 7.433 0.081 46.0 reviewed us us
26641 2023-12-29T16:31:16.656Z 25.1050 96.5309 10.000 5.00 mb 53.0 64.0 4.1560 0.78 ... 2023-12-29T16:45:27.040Z 92 km WSW of Myitkyina, Myanmar earthquake 7.85 1.876 0.071 64.0 reviewed us us

21080 rows × 22 columns

In [4]:
df.time
Out[4]:
0        2023-01-01T00:49:25.294Z
1        2023-01-01T01:41:43.755Z
2        2023-01-01T03:29:31.070Z
4        2023-01-01T04:29:13.793Z
5        2023-01-01T04:50:17.639Z
                   ...           
26637    2023-12-29T03:37:19.334Z
26638    2023-12-29T04:38:54.109Z
26639    2023-12-29T08:42:05.747Z
26640    2023-12-29T11:02:48.679Z
26641    2023-12-29T16:31:16.656Z
Name: time, Length: 21080, dtype: object

Dans un contexte de nettoyage et de prétraitement des données, nous avons encodé les variables catégorielles afin qu'elles soient dans un format compréhensible par les algorithmes d'apprentissage automatique.

In [5]:
df.info()
<class 'pandas.core.frame.DataFrame'>
Index: 21080 entries, 0 to 26641
Data columns (total 22 columns):
 #   Column           Non-Null Count  Dtype  
---  ------           --------------  -----  
 0   time             21080 non-null  object 
 1   latitude         21080 non-null  float64
 2   longitude        21080 non-null  float64
 3   depth            21080 non-null  float64
 4   mag              21080 non-null  float64
 5   magType          21080 non-null  object 
 6   nst              21080 non-null  float64
 7   gap              21080 non-null  float64
 8   dmin             21080 non-null  float64
 9   rms              21080 non-null  float64
 10  net              21080 non-null  object 
 11  id               21080 non-null  object 
 12  updated          21080 non-null  object 
 13  place            21080 non-null  object 
 14  type             21080 non-null  object 
 15  horizontalError  21080 non-null  float64
 16  depthError       21080 non-null  float64
 17  magError         21080 non-null  float64
 18  magNst           21080 non-null  float64
 19  status           21080 non-null  object 
 20  locationSource   21080 non-null  object 
 21  magSource        21080 non-null  object 
dtypes: float64(12), object(10)
memory usage: 3.7+ MB

Nous normalisons les colonnes time et updated afin qu'elles soient plus lisibles mais également pour que nous puissions encoder les variables catégorielles sans supprimer ces données.

In [6]:
df['time'] = pd.to_datetime(df['time'], format='%Y-%m-%dT%H:%M:%S.%fZ')

df['updated'] = pd.to_datetime(df['updated'], format='%Y-%m-%dT%H:%M:%S.%fZ')

df['time']

df['updated']
C:\Users\Killian\AppData\Local\Temp\ipykernel_20516\1747763810.py:1: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  df['time'] = pd.to_datetime(df['time'], format='%Y-%m-%dT%H:%M:%S.%fZ')
C:\Users\Killian\AppData\Local\Temp\ipykernel_20516\1747763810.py:3: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  df['updated'] = pd.to_datetime(df['updated'], format='%Y-%m-%dT%H:%M:%S.%fZ')
Out[6]:
0       2023-03-11 22:51:52.040
1       2023-03-11 22:51:45.040
2       2023-03-11 22:51:29.040
4       2023-03-11 22:51:38.040
5       2023-03-11 22:51:29.040
                  ...          
26637   2023-12-29 04:05:57.040
26638   2023-12-29 10:59:44.533
26639   2023-12-29 08:57:05.040
26640   2023-12-29 11:22:46.040
26641   2023-12-29 16:45:27.040
Name: updated, Length: 21080, dtype: datetime64[ns]
In [7]:
from sklearn.preprocessing import LabelEncoder

label_encoder = LabelEncoder()

colonnes_a_exclure = ['place', 'id', 'type']

for colonne in df.columns:
    if colonne not in colonnes_a_exclure and df[colonne].dtypes == 'object':
        df[colonne] = label_encoder.fit_transform(df[colonne])
        print(df[colonne])

df.info()
0        3
1        0
2        2
4        3
5        3
        ..
26637    0
26638    0
26639    0
26640    0
26641    0
Name: magType, Length: 21080, dtype: int32
0        7
1        7
2        4
4        7
5        7
        ..
26637    7
26638    7
26639    7
26640    7
26641    7
Name: net, Length: 21080, dtype: int32
0        1
1        1
2        1
4        1
5        1
        ..
26637    1
26638    1
26639    1
26640    1
26641    1
Name: status, Length: 21080, dtype: int32
0        7
1        7
2        4
4        7
5        7
        ..
26637    7
26638    7
26639    7
26640    7
26641    7
Name: locationSource, Length: 21080, dtype: int32
0        7
1        7
2        4
4        7
5        7
        ..
26637    7
26638    7
26639    7
26640    7
26641    7
Name: magSource, Length: 21080, dtype: int32
<class 'pandas.core.frame.DataFrame'>
Index: 21080 entries, 0 to 26641
Data columns (total 22 columns):
 #   Column           Non-Null Count  Dtype         
---  ------           --------------  -----         
 0   time             21080 non-null  datetime64[ns]
 1   latitude         21080 non-null  float64       
 2   longitude        21080 non-null  float64       
 3   depth            21080 non-null  float64       
 4   mag              21080 non-null  float64       
 5   magType          21080 non-null  int32         
 6   nst              21080 non-null  float64       
 7   gap              21080 non-null  float64       
 8   dmin             21080 non-null  float64       
 9   rms              21080 non-null  float64       
 10  net              21080 non-null  int32         
 11  id               21080 non-null  object        
 12  updated          21080 non-null  datetime64[ns]
 13  place            21080 non-null  object        
 14  type             21080 non-null  object        
 15  horizontalError  21080 non-null  float64       
 16  depthError       21080 non-null  float64       
 17  magError         21080 non-null  float64       
 18  magNst           21080 non-null  float64       
 19  status           21080 non-null  int32         
 20  locationSource   21080 non-null  int32         
 21  magSource        21080 non-null  int32         
dtypes: datetime64[ns](2), float64(12), int32(5), object(3)
memory usage: 3.3+ MB
C:\Users\Killian\AppData\Local\Temp\ipykernel_20516\3547750565.py:9: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  df[colonne] = label_encoder.fit_transform(df[colonne])
C:\Users\Killian\AppData\Local\Temp\ipykernel_20516\3547750565.py:9: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  df[colonne] = label_encoder.fit_transform(df[colonne])
C:\Users\Killian\AppData\Local\Temp\ipykernel_20516\3547750565.py:9: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  df[colonne] = label_encoder.fit_transform(df[colonne])
C:\Users\Killian\AppData\Local\Temp\ipykernel_20516\3547750565.py:9: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  df[colonne] = label_encoder.fit_transform(df[colonne])
C:\Users\Killian\AppData\Local\Temp\ipykernel_20516\3547750565.py:9: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  df[colonne] = label_encoder.fit_transform(df[colonne])

Nous avons encodé les variables catégorielles afin qu'elles soient dans un format compréhensible par les algorithmes d'apprentissage automatique

In [8]:
df.info()
<class 'pandas.core.frame.DataFrame'>
Index: 21080 entries, 0 to 26641
Data columns (total 22 columns):
 #   Column           Non-Null Count  Dtype         
---  ------           --------------  -----         
 0   time             21080 non-null  datetime64[ns]
 1   latitude         21080 non-null  float64       
 2   longitude        21080 non-null  float64       
 3   depth            21080 non-null  float64       
 4   mag              21080 non-null  float64       
 5   magType          21080 non-null  int32         
 6   nst              21080 non-null  float64       
 7   gap              21080 non-null  float64       
 8   dmin             21080 non-null  float64       
 9   rms              21080 non-null  float64       
 10  net              21080 non-null  int32         
 11  id               21080 non-null  object        
 12  updated          21080 non-null  datetime64[ns]
 13  place            21080 non-null  object        
 14  type             21080 non-null  object        
 15  horizontalError  21080 non-null  float64       
 16  depthError       21080 non-null  float64       
 17  magError         21080 non-null  float64       
 18  magNst           21080 non-null  float64       
 19  status           21080 non-null  int32         
 20  locationSource   21080 non-null  int32         
 21  magSource        21080 non-null  int32         
dtypes: datetime64[ns](2), float64(12), int32(5), object(3)
memory usage: 3.3+ MB

2 - Analyse exploratoire des données (EDA)¶

Pour effectuez une analyse descriptive des données, nous allons commencer par résumé les variables numériques

In [9]:
print(df.describe())
                                time      latitude     longitude  \
count                          21080  21080.000000  21080.000000   
mean   2023-06-28 14:41:49.917795840     15.680956     -3.106846   
min       2023-01-01 00:49:25.294000    -65.849700   -179.998700   
25%    2023-03-25 19:21:46.375500032     -6.513600   -118.385000   
50%    2023-06-25 20:33:20.763500032     18.227500    -26.733400   
75%    2023-10-01 13:39:05.907749888     38.858675    127.268800   
max       2023-12-29 23:17:18.800000     86.593900    179.999400   
std                              NaN     29.314531    128.808562   

              depth           mag       magType           nst           gap  \
count  21080.000000  21080.000000  21080.000000  21080.000000  21080.000000   
mean      68.321158      4.048470      1.605408     42.528795    127.081074   
min       -3.370000      2.600000      0.000000      3.000000      8.000000   
25%       10.000000      3.370000      0.000000     18.000000     73.000000   
50%       21.532000      4.300000      0.000000     29.000000    113.000000   
75%       66.333250      4.500000      3.000000     52.000000    172.000000   
max      681.238000      7.800000     11.000000    423.000000    350.000000   
std      118.155052      0.786942      2.833168     38.923838     69.076922   

               dmin           rms           net  \
count  21080.000000  21080.000000  21080.000000   
mean       2.551639      0.578899      6.551044   
min        0.000000      0.010000      0.000000   
25%        0.576975      0.400000      7.000000   
50%        1.491000      0.580000      7.000000   
75%        3.074000      0.750000      7.000000   
max       50.820000      1.880000      9.000000   
std        3.805738      0.255945      1.297837   

                             updated  horizontalError    depthError  \
count                          21080     21080.000000  21080.000000   
mean   2023-08-24 08:20:55.669221120         6.963787      4.800911   
min       2023-01-01 11:14:15.230000         0.000000      0.100000   
25%    2023-05-26 21:54:07.790000128         4.100000      1.878000   
50%       2023-08-26 20:54:24.040000         6.980000      2.693000   
75%    2023-11-28 18:44:39.960250112         9.620000      6.955250   
max       2023-12-30 00:23:54.040000        99.000000     47.878000   
std                              NaN         4.037148      4.577801   

           magError        magNst        status  locationSource     magSource  
count  21080.000000  21080.000000  21080.000000    21080.000000  21080.000000  
mean       0.118441     33.598482      0.999573        6.551044      6.551044  
min        0.000000      0.000000      0.000000        0.000000      0.000000  
25%        0.078000     10.000000      1.000000        7.000000      7.000000  
50%        0.108000     18.000000      1.000000        7.000000      7.000000  
75%        0.148000     36.000000      1.000000        7.000000      7.000000  
max        0.770000    884.000000      1.000000        9.000000      9.000000  
std        0.058194     49.353409      0.020659        1.297837      1.297837  

Nous remarquons qu'il y a quelques valeurs aberrantes comme par exemple l'écart entre la valeur moyenne et la valeur max de la colonne magNst.

Nous pouvons aussi obtenir la fréquence des différentes catégories

In [10]:
print(df.magNst.value_counts())
magNst
10.0     1124
12.0     1012
8.0       989
14.0      933
6.0       862
         ... 
273.0       1
457.0       1
500.0       1
257.0       1
404.0       1
Name: count, Length: 379, dtype: int64

D'autre part, nous pouvons obtenir la matrice de corrélation entre les variables numériques pour corréler les valeurs.

In [11]:
colonne_num = df.select_dtypes(include='number')
correlation_matrix = colonne_num.corr()

print(correlation_matrix)
                 latitude  longitude     depth       mag   magType       nst  \
latitude         1.000000  -0.180172 -0.279452 -0.534165  0.174312 -0.002172   
longitude       -0.180172   1.000000 -0.054961  0.491289 -0.164641  0.151647   
depth           -0.279452  -0.054961  1.000000  0.143370 -0.135157  0.064436   
mag             -0.534165   0.491289  0.143370  1.000000  0.009480  0.528265   
magType          0.174312  -0.164641 -0.135157  0.009480  1.000000  0.405277   
nst             -0.002172   0.151647  0.064436  0.528265  0.405277  1.000000   
gap              0.311072  -0.299297 -0.179294 -0.540287 -0.036322 -0.476688   
dmin            -0.355968   0.186362  0.041044  0.354928 -0.108273  0.068382   
rms             -0.219160   0.213494  0.097704  0.494904 -0.077533  0.183966   
net             -0.126499   0.233183  0.136797  0.413283 -0.090965  0.090476   
horizontalError -0.411654   0.283793  0.415469  0.470323 -0.254747  0.022009   
depthError      -0.034863  -0.034154  0.326831 -0.036556 -0.101855 -0.142591   
magError        -0.156546   0.014150 -0.036732 -0.133314 -0.277945 -0.430332   
magNst           0.027111   0.043381  0.099790  0.247017 -0.090177  0.499504   
status          -0.014022   0.018107  0.011054  0.032293 -0.009363  0.008422   
locationSource  -0.126499   0.233183  0.136797  0.413283 -0.090965  0.090476   
magSource       -0.126499   0.233183  0.136797  0.413283 -0.090965  0.090476   

                      gap      dmin       rms       net  horizontalError  \
latitude         0.311072 -0.355968 -0.219160 -0.126499        -0.411654   
longitude       -0.299297  0.186362  0.213494  0.233183         0.283793   
depth           -0.179294  0.041044  0.097704  0.136797         0.415469   
mag             -0.540287  0.354928  0.494904  0.413283         0.470323   
magType         -0.036322 -0.108273 -0.077533 -0.090965        -0.254747   
nst             -0.476688  0.068382  0.183966  0.090476         0.022009   
gap              1.000000 -0.168113 -0.262887 -0.172500        -0.130615   
dmin            -0.168113  1.000000  0.145757  0.202912         0.432698   
rms             -0.262887  0.145757  1.000000  0.486624         0.334725   
net             -0.172500  0.202912  0.486624  1.000000         0.480372   
horizontalError -0.130615  0.432698  0.334725  0.480372         1.000000   
depthError       0.239580 -0.080188  0.017430  0.000588         0.227445   
magError         0.069712 -0.010270 -0.058038 -0.075433         0.044140   
magNst          -0.296739  0.041580  0.099376 -0.015280         0.016140   
status           0.000955  0.013104  0.031942  0.054780         0.029911   
locationSource  -0.172500  0.202912  0.486624  1.000000         0.480372   
magSource       -0.172500  0.202912  0.486624  1.000000         0.480372   

                 depthError  magError    magNst    status  locationSource  \
latitude          -0.034863 -0.156546  0.027111 -0.014022       -0.126499   
longitude         -0.034154  0.014150  0.043381  0.018107        0.233183   
depth              0.326831 -0.036732  0.099790  0.011054        0.136797   
mag               -0.036556 -0.133314  0.247017  0.032293        0.413283   
magType           -0.101855 -0.277945 -0.090177 -0.009363       -0.090965   
nst               -0.142591 -0.430332  0.499504  0.008422        0.090476   
gap                0.239580  0.069712 -0.296739  0.000955       -0.172500   
dmin              -0.080188 -0.010270  0.041580  0.013104        0.202912   
rms                0.017430 -0.058038  0.099376  0.031942        0.486624   
net                0.000588 -0.075433 -0.015280  0.054780        1.000000   
horizontalError    0.227445  0.044140  0.016140  0.029911        0.480372   
depthError         1.000000  0.013334 -0.075370 -0.009574        0.000588   
magError           0.013334  1.000000 -0.427177 -0.020402       -0.075433   
magNst            -0.075370 -0.427177  1.000000  0.002391       -0.015280   
status            -0.009574 -0.020402  0.002391  1.000000        0.054780   
locationSource     0.000588 -0.075433 -0.015280  0.054780        1.000000   
magSource          0.000588 -0.075433 -0.015280  0.054780        1.000000   

                 magSource  
latitude         -0.126499  
longitude         0.233183  
depth             0.136797  
mag               0.413283  
magType          -0.090965  
nst               0.090476  
gap              -0.172500  
dmin              0.202912  
rms               0.486624  
net               1.000000  
horizontalError   0.480372  
depthError        0.000588  
magError         -0.075433  
magNst           -0.015280  
status            0.054780  
locationSource    1.000000  
magSource         1.000000  

Une matrice de corrélation nous indique plusieurs choses. Les valeurs vont de -1 à 1 :

1 indique une corrélation positive parfaite. -1 indique une corrélation négative parfaite. 0 indique aucune corrélation.

Par exemple :

Erreur horizontale dans la détermination de l'emplacement (horizontalError) vs. Profondeur (depth) : Il y a une corrélation positive moyenne (0,42), montrant qu'à mesure que la profondeur du séisme augmente, l'erreur horizontale dans la détermination de l'emplacement a aussi tendance à augmenter.

Enfin, nous pouvons obtenir des statistiques temporelles pour mieux situer nos données dans le temps

In [12]:
time_stat = df['time'].describe()
print(time_stat)

update_stat = df['updated'].describe()
print(update_stat)
count                            21080
mean     2023-06-28 14:41:49.917795840
min         2023-01-01 00:49:25.294000
25%      2023-03-25 19:21:46.375500032
50%      2023-06-25 20:33:20.763500032
75%      2023-10-01 13:39:05.907749888
max         2023-12-29 23:17:18.800000
Name: time, dtype: object
count                            21080
mean     2023-08-24 08:20:55.669221120
min         2023-01-01 11:14:15.230000
25%      2023-05-26 21:54:07.790000128
50%         2023-08-26 20:54:24.040000
75%      2023-11-28 18:44:39.960250112
max         2023-12-30 00:23:54.040000
Name: updated, dtype: object

3 - Visualisation¶

Suite à ces analyses, nous allons tracer un boxplot pour visualiser avec précisions ces valeurs aberrantes.

Cette étape nous servira dans notre choix de modèle de machine learning.

In [13]:
import matplotlib.pyplot as plt
import seaborn as sns

plt.figure(figsize=(12, 6))
sns.boxplot(data=df)
plt.xticks(rotation=45)  
plt.show()

Cette visualisation nous permet de nous rendre compte des écarts possibles entre les valeurs moyennes/minimums/maximum. De plus nous voyons que plus les boxs sont étirées plus il y a de valeurs abérrantes. (Nous confirmons bien que magNstet depthcontiennt des valeurs abérrantes)

On peut aussi analyser les types de catastrophes sysmiques afin de mieux comprendre le jeu de données et rendre nos analyses plus pertinentes.

In [14]:
import plotly.express as px
earthquake_type = df['type'].value_counts()

fig = px.bar(y = earthquake_type.values, x = earthquake_type.index, color = earthquake_type.index, text = earthquake_type.values)
fig.show()

Nous voyons qu'il y a une grande majorité d'earthquake dans notre dataframe. Nous allons donc choisir ce type pour notre algorithme

Afin de mieux comprendre les données, on a choisi de mettre sous forme de nuage de points la magnitude en fonction de la date du séisme.

In [15]:
plt.figure(figsize=(10, 6))

sns.scatterplot(x='time', y='mag', data=df, s=50)

plt.xlabel('date')
plt.ylabel('magnitude')
plt.show()

On peut voir que des séismes de forte magnitude (>6.5) ont lieux régulièrements dans l'année, cependant on note une tendance plus élevée en début d'année 2023. D'autre part, on voit que la plupart des séismes ont une magnitude moyenne (entre 4 & 5)

On va s'intéresser au tremblement de terre de magnitude supérieur à 5 afin de voir les zones à haut risque de tremblement dévastateur.

In [16]:
import folium

carte = folium.Map(location=[43.327408, -1.032999], zoom_start = 2)
for ind, i in df.iterrows():
    if i['type'] == 'earthquake' and i['mag'] > 5:
        carte.add_child(folium.RegularPolygonMarker(location = [i['latitude'], i['longitude']], popup = i['place'], fill_color = 'red', radius = 10))

    
carte
Out[16]:
Make this Notebook Trusted to load map: File -> Trust Notebook

On remarque que l'océan Pacifique est l'endroit où les tremblements de terre ont tendance à être plus forts que la moyenne (magnitude > 5)

4 - Sélection de caractéristiques cibles¶

Au vue des explorations effectuées, nous voulons prédire le nombre de séismes avec une magnitude supérieure à 5.

5 - Sélection du modèle¶

Nous avons vu en cours plusieurs algorithmes de machine learning. Nous allons évaluer ces algorithmes pour choisir celui qui correspond le mieux à notre besoin.

La régression linéaire simple¶

On cherche à expliquer les variations d’une variable dépendante Y (variable réponse) avec celles d’une ou plusieurs variables indépendantes (variables explicatives). Pour cela on modélise une relation linéaire. Pour la régression linéaire simple une seule variable indépendante est utilisée pour prédire une variable dépendante.

La régression linéaire multiple¶

C'est un peu près la même chose que la la régression linéaire simple mais cette fois plusieurs variables indépendantes sont utilisées pour prédire une variable dépendante.

Ce dernier n'est pas applicable pour notre prédiction étant donné que l'on utilise une seule variable indépendante.

La régression logistique¶

C’est un modèle d'apprentissage supervisé qui utilise des données étiquetées pour faire des prédictions. Il utilise l'estimation du maximum de vraisemblance (MLE) qui détermine les paramètres (moyenne et variance) permettant de maximiser la probabilité de produire le résultat souhaité.

K-means¶

Le principe de l'algorithme K-means est de s'entrainer sur un jeu de données sans réponses préexistante, il tire des conclusions sur des données non étiquetées. On peut l'utiliser dans des cas comme la reconnaissance vocale, le classement des données ou même la compression d'images , etc ..

Cependant cet algorithme n'est pas applicable dans notre cas. En effet ce dernier vise à séparer nos données en clusters et ne sert donc pas pour notre prédiction.

KNN¶

Les K-plus proches voisins (KNN) représentent une catégorie d'algorithmes d'apprentissage supervisé exploités à des fins de régression et de classification, leur utilisation principale se concentre sur des problèmes de classification.

HCA¶

L'algorithme d'analyse hiérarchique ascendante (HCA) organise les données de manière hiérarchique en fusionnant itérativement les clusters les plus similaires, offrant ainsi une visualisation structurelle via un dendrogramme et permettant une analyse approfondie de la similarité entre les observations.

L'algorithme n'est pas applicable à notre cas. En effet ce dernier n'est pas orienté pour de la prédiction mais plus pour de la classification hiérarchique de données.

Évaluation des algorithmes¶

In [17]:
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import MinMaxScaler
from sklearn.linear_model import LinearRegression, LogisticRegression
from sklearn.neighbors import KNeighborsClassifier, KNeighborsRegressor
from sklearn.preprocessing import LabelEncoder
from sklearn.metrics import accuracy_score, confusion_matrix, mean_squared_error, classification_report, precision_score, f1_score, recall_score

Création des données d'entrainement :

In [18]:
X = df[['latitude', 'longitude', 'mag']]
y = (df['mag'] > 5).astype(int)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.7, random_state = 42)

On normalise nos données :

In [19]:
scaler = MinMaxScaler()
X_train_norm = scaler.fit_transform(X_train)
X_test_norm = scaler.transform(X_test)

On les encode :

In [20]:
label_encoder = LabelEncoder()
y = label_encoder.fit_transform(y)

On met en place la Régression linéaire pour l'évaluer :

In [21]:
regression_lineaire = LinearRegression()
regression_lineaire.fit(X_train_norm, y_train)
y_pred_lineaire = regression_lineaire.predict(X_test_norm)

On met en place la Régression logistique pour l'évaluer :

In [22]:
regression_logistique = LogisticRegression()
regression_logistique.fit(X_train_norm, y_train)
y_pred_logistique = regression_logistique.predict(X_test_norm)

On met en place KNN pour l'évaluer :

In [23]:
knn_model = KNeighborsClassifier(n_neighbors=3)
knn_model.fit(X_train_norm, y_train)
y_pred_knn = knn_model.predict(X_test_norm)
Evaluation de l'erreur quadratique et de la précision des modèles :¶
In [24]:
precision_logistique = accuracy_score(y_test, y_pred_logistique)
precision_knn = accuracy_score(y_test, y_pred_knn)

print(f'Erreur quadratique (Régression Linéaire) : {mean_squared_error(y_test, y_pred_lineaire)}\n')

print(f'Précision (Régression Logistique) : {precision_logistique * 100}')
print(f'Erreur quadratique (Régression Logistique) : {mean_squared_error(y_test, y_pred_logistique)}\n')

print(f'Précision (KNN) : {precision_knn * 100}')
print(f'Erreur quadratique (KNN) : {mean_squared_error(y_test, y_pred_knn)}')
Erreur quadratique (Régression Linéaire) : 0.043035068198020276

Précision (Régression Logistique) : 96.76063973976687
Erreur quadratique (Régression Logistique) : 0.032393602602331255

Précision (KNN) : 99.39007861209
Erreur quadratique (KNN) : 0.006099213879100027
In [25]:
confusion_mat_reg_logistique = confusion_matrix(y_test, y_pred_logistique)
sns.heatmap(confusion_mat_reg_logistique, annot = True, fmt = 'd', cmap = 'Blues', xticklabels = ['Magnitude inférieure à 5', 'Magnitude supérieure à 5'], yticklabels = ['Magnitude inférieure à 5', 'Magnitude supérieure à 5'])
plt.title('Matrice de confusion Régression Logistique')
plt.xlabel('Prédictions')
plt.ylabel('Vraies valeurs')
plt.show()

confusion_mat_knn = confusion_matrix(y_test, y_pred_knn)
sns.heatmap(confusion_mat_knn, annot = True,  fmt = 'd', cmap = 'Blues', xticklabels = ['Magnitude inférieure à 5', 'Magnitude supérieure à 5'], yticklabels = ['Magnitude inférieure à 5', 'Magnitude supérieure à 5'])
plt.title('Matrice de confusion KNN')
plt.xlabel('Prédictions')
plt.ylabel('Vraies valeurs')
plt.show()

Nous remarquons alors que l'algorithme KNN nous permet d'avoir plus de prédiction sur la caractéristique ciblée.

Conclusion :¶

Compte tenu de notre but : prédir le nombre de tremblement de terre de magnitude supérieur à 5 nous avons choisi l'algorithme : KNN. En effet, cet algorithme est plus précit pour notre problème de prédiction.

6 - Entraînement du modèle¶

On peut par exemple utiliser la latitude, la longitude, et d'autres caractéristiques comme variables explicatives pour prédire si un tremblement de terre de haute intensité se produira.

On vérifie que notre dataframe ne contient pas de valeurs null

In [26]:
df.isnull().sum()
Out[26]:
time               0
latitude           0
longitude          0
depth              0
mag                0
magType            0
nst                0
gap                0
dmin               0
rms                0
net                0
id                 0
updated            0
place              0
type               0
horizontalError    0
depthError         0
magError           0
magNst             0
status             0
locationSource     0
magSource          0
dtype: int64

On veut alors prédire le nombre de tremblements de terre avec une magnitude supérieure à 5 :

On créer alors nos ensembles de données d'entraînement et de test :

In [27]:
X = df[['latitude', 'longitude', 'mag']]
y = (df['mag'] > 5).astype(int)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.7, random_state = 42)

On réalise une mise à l'échelle

In [28]:
scaler = MinMaxScaler()
X_train_norm = scaler.fit_transform(X_train)
X_test_norm = scaler.transform(X_test)

On procède à l'encodage de nos données : (0 si la magnitude est inférieure à 5 sinon 1)

In [29]:
label_encoder = LabelEncoder()
y = label_encoder.fit_transform(y)
print(y)
[0 0 0 ... 1 0 0]

On applique notre algorithme KNN, pour entrainer le modèle :

In [30]:
knn_model = KNeighborsClassifier(n_neighbors=3)
knn_model.fit(X_train_norm, y_train)
y_pred_knn = knn_model.predict(X_test_norm)

On prédit alors nos résultats en faisant intervenir une matrice de confusion pour vérifier les prédictions.

In [31]:
confusion_mat_knn = confusion_matrix(y_test, y_pred_knn)
sns.heatmap(confusion_mat_knn, annot = True, fmt = 'd', cmap = 'Blues', xticklabels = ['Magnitude inférieure à 5', 'Magnitude supérieure à 5'], yticklabels = ['Magnitude inférieure à 5', 'Magnitude supérieure à 5'])
plt.title('Matrice de confusion KNN')
plt.xlabel('Prédictions')
plt.ylabel('Vraies valeurs')
plt.show()

On voit qu'on arrive à prédire 817 séismes avec une magnitude supérieure à 5

Comme très peu de tremblements de terre ont une magnitude supérieure à 5, on arrive avec précision (≃ 100%) à prédire le nombre de tremblements de terre de ce type dans le futur.

On va maintenant l'entrainer pour avoir des prédictions plus juste :

Pour ce faire on utilise l'outil GridSearchCV :

In [32]:
from sklearn.model_selection import GridSearchCV

param_grid = {'n_neighbors': [3, 5, 7, 9]}
grid_search = GridSearchCV(knn_model, param_grid, scoring='accuracy')
grid_search.fit(X_train_norm, y_train)

meilleurs_parametres = grid_search.best_params_
print(f'Meilleurs hyperparamètres : {meilleurs_parametres}')

meilleur_algo = grid_search.best_estimator_

y_pred_knn2 = meilleur_algo.predict(X_test_norm)
precision_knn2 = accuracy_score(y_test, y_pred_knn2)
print(f'Précision : {precision_knn2 * 100} %')
Meilleurs hyperparamètres : {'n_neighbors': 3}
Précision : 99.39007861209 %

On voit que nos prévisions sont les plus précises avec un paramétrage à k = 3

7 - Evaluation du modèle¶

Afin d'évaluer notre modèle, on utilise une métrique se nommant RMSE (Root Mean Square Error).

In [33]:
mse = mean_squared_error(y_test, y_pred_knn)
print(f'Mean Squared Error: {mse}')
Mean Squared Error: 0.006099213879100027

On voit que comme notre erreur quadratique est proche de 0 nos prédictions sont précises.

On peut de plus utiliser les métriques RECALL/F1-SCORE/ Précision pour mieux évaluer notre modèle :

In [34]:
print(classification_report(y_test,y_pred_knn))
              precision    recall  f1-score   support

           0       0.99      1.00      1.00     13855
           1       0.99      0.91      0.95       901

    accuracy                           0.99     14756
   macro avg       0.99      0.95      0.97     14756
weighted avg       0.99      0.99      0.99     14756

Toutes ces valeurs étant basses, nous indiquent une bonne fiabilité de notre algorithme dans nos prédictions.

In [35]:
faux_positif = confusion_mat_knn[0, 1] / (confusion_mat_knn[0, 1] +
confusion_mat_knn[1, 1])
print('Taux de Faux Positifs:', faux_positif * 100)
Taux de Faux Positifs: 0.7290400972053462

De plus, nous voyons que nous avons un très faible taux de faux positif. Ce qui appui d'autant plus la fiabilité de notre algorithme.

8 - Optimisation, maintenance et mise à jour¶

Optimisation du modèle :¶

Dans l'algorithme KNN, on peut optimiser plusieurs paramètres : nombre de voisins, la distance utilisée, etc.

C'est ce que nous cherchons à faire dans cette partie :

In [36]:
param_grid = {'n_neighbors': [3, 5, 7, 9], 'metric': ['euclidean', 'manhattan', 'minkowski', 'cosinus']}

grid_search = GridSearchCV(knn_model, param_grid, scoring='accuracy')
grid_search.fit(X_train_norm, y_train)

Parametres_opti = grid_search.best_params_
print(f'Meilleurs hyperparamètres : {Parametres_opti}')

meilleur_algo = grid_search.best_estimator_

y_pred_knn3 = meilleur_algo.predict(X_test_norm)
precision_knn3 = accuracy_score(y_test, y_pred_knn3)
print(f'Précision (KNN avec meilleurs paramètres) : {precision_knn3 * 100}')
D:\anaconda3\Lib\site-packages\sklearn\model_selection\_validation.py:378: FitFailedWarning:


20 fits failed out of a total of 80.
The score on these train-test partitions for these parameters will be set to nan.
If these failures are not expected, you can try to debug them by setting error_score='raise'.

Below are more details about the failures:
--------------------------------------------------------------------------------
20 fits failed with the following error:
Traceback (most recent call last):
  File "D:\anaconda3\Lib\site-packages\sklearn\model_selection\_validation.py", line 686, in _fit_and_score
    estimator.fit(X_train, y_train, **fit_params)
  File "D:\anaconda3\Lib\site-packages\sklearn\neighbors\_classification.py", line 207, in fit
    return self._fit(X, y)
           ^^^^^^^^^^^^^^^
  File "D:\anaconda3\Lib\site-packages\sklearn\neighbors\_base.py", line 446, in _fit
    self._check_algorithm_metric()
  File "D:\anaconda3\Lib\site-packages\sklearn\neighbors\_base.py", line 381, in _check_algorithm_metric
    raise ValueError(
ValueError: Metric 'cosinus' not valid. Use sorted(sklearn.neighbors.VALID_METRICS['brute']) to get valid options. Metric can also be a callable function.


D:\anaconda3\Lib\site-packages\sklearn\model_selection\_search.py:953: UserWarning:

One or more of the test scores are non-finite: [0.99399072 0.99351616 0.99161868 0.99082804 0.99399072 0.99351629
 0.99177703 0.9900379  0.99399072 0.99351616 0.99161868 0.99082804
        nan        nan        nan        nan]

Meilleurs hyperparamètres : {'metric': 'euclidean', 'n_neighbors': 3}
Précision (KNN avec meilleurs paramètres) : 99.39007861209

Maintenance du modèle :¶

In [37]:
if faux_positif > 0.01:
    print('L\'intégrité de vos données se dégrade, veuillez réctifier votre modèle ')
else:
    print(f'Votre modèle propose des données cohérentes, le taux de faux positif est de {faux_positif * 100} %')
Votre modèle propose des données cohérentes, le taux de faux positif est de 0.7290400972053462 %

Mise à jour des données :¶

Nos données provenant du dataset earthquake_2023, nous pourrons alimenter notre algorithme de prédiction avec de futurs données (Ex : Tremblements de terre 2024)